package com.bjnet.castdemo;

import android.os.Handler;
import android.os.Message;

import com.bjnet.castdemo.airplay.imp.AirplayModuleImp;
import com.bjnet.castdemo.base.MediaChannelCtx;
import com.bjnet.castdemo.bjcast.imp.BJCastModuleImp;
import com.bjnet.castdemo.bjcast.imp.GLScreenRenderChannel;
import com.bjnet.castdemo.event.RefreshPinEvent;
import com.bjnet.castdemo.event.RenameEvent;
import com.bjnet.cbox.module.AirplayModule;
import com.bjnet.cbox.module.BJCastModule;
import com.bjnet.cbox.module.MediaChannel;
import com.blankj.utilcode.util.LogUtils;

import org.greenrobot.eventbus.Subscribe;
import org.greenrobot.eventbus.ThreadMode;

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.Properties;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

/**
 * Created by supermanwg on 2016/10/7.
 */
public class CastManager {
    private static int MSG_NO_SEED = 0;
//    public static final int MSG_CHANNEL_CLOSED = MSG_NO_SEED++;
//    public static final int MSG_CHANNEL_UPDATE_PICTURE = MSG_NO_SEED++;

    public static final int MSG_START_MIRROR_DISPLAY = MSG_NO_SEED++;
    public static final int MSG_SURFACE_READY = MSG_NO_SEED++;
    public static final int MSG_SURFACE_DESTROYED = MSG_NO_SEED++;
    public static final int MSG_UI_SURFACE_CREATED = MSG_NO_SEED++;
    public static final int MSG_UI_SURFACE_DESTROYED = MSG_NO_SEED++;
    //public static final int MSG_CHANNEL_CONTENT_SIZE_CHANGED = MSG_NO_SEED++;
    //public static final int MSG_CHANNEL_VIDEO_ROTATE_CHANGED = MSG_NO_SEED++;

    private ConcurrentHashMap<Integer, MediaChannelCtx> channelCtxMap;
    private String pincode = "";
    private static CastManager instance = null;
    private static int idSeed = 0;
    private boolean isEnablePin = false; //is enable pin code

    private final String restart_udhcp_with_dns = "/tmp/restart_udhcpd1";
    private final String restart_udhcp_no_dns = "/tmp/restart_udhcpd0";
    private final int BJ_UDHCPD_CTRL_PORT = 60111;

    public boolean isFirstUpgrade() {
        return isFirstUpgrade;
    }

    public void setFirstUpgrade(boolean firstUpgrade) {
        isFirstUpgrade = firstUpgrade;
    }

    private boolean isFirstUpgrade = true;

    public BJCastModule getCastModule() {
        return castModule;
    }

    private BJCastModule castModule = null;

    public AirplayModule getAirplayModule() {
        return airplayModule;
    }

    public void setAirplayModule(AirplayModule airplayModule) {
        this.airplayModule = airplayModule;
    }

    private AirplayModule airplayModule = null;

    private boolean networkStatusConnected = false;

    public Handler getUiHandler() {
        return uiHandler;
    }

    public void setUiHandler(Handler uiHandler) {
        this.uiHandler = uiHandler;
    }

    public void setSvcHandler(Handler handler) {
        this.svcHandler = handler;
    }

    private Handler uiHandler = null;
    private Handler svcHandler = null;
//    public MediaChannel getCurRenderChannel() {
//        return curRenderChannel;
//    }


    public MediaChannelCtx getChannelCtxById(int channelId) {
        //return curRenderChannel;
        if (channelCtxMap.containsKey(channelId)) {
            return channelCtxMap.get(channelId);
        }
        return null;
    }

    public MediaChannel getChannelById(int channelId) {
        //return curRenderChannel;
        if (channelCtxMap.containsKey(channelId)) {
            return channelCtxMap.get(channelId).getChannel();
        }
        return null;
    }

//    private MediaChannel curRenderChannel = null;

    private CastManager() {
//        this.curRenderChannel = null;
        channelCtxMap = new ConcurrentHashMap<>();
        DemoApplication.APP.getEventBus().register(this);
    }

    public void postMessageToSvc(Message msg) {
        if (this.svcHandler != null) {
            this.svcHandler.sendMessage(msg);
        }
    }

    private int generateChannelID() {
        return idSeed++;
    }

    public void registerChannel(MediaChannel channel) {
        if (channel.getChannelId() == -1) {
            int id = generateChannelID();
            channel.setChannelId(id);
        }

        MediaChannelCtx ctx = new MediaChannelCtx(channel);
//        curRenderChannel = channel;
        this.channelCtxMap.put(channel.getChannelId(), ctx);
        LogUtils.dTag(DemoApplication.TAG, "registerChannel id = " + channel.getChannelId());
    }

    public void updateChannelFlag(int channelId, int flag) {
        LogUtils.dTag(DemoApplication.TAG, "updateChannelFlag id = " + channelId + " flag=" + flag);
        synchronized (this.channelCtxMap) {
            if (this.channelCtxMap.containsKey(channelId)) {
                MediaChannelCtx ctx = this.channelCtxMap.get(channelId);
                if (ctx != null) {
                    int mask = ctx.getChannelMask();
                    int newMask = mask | flag;
                    ctx.setChannelMask(newMask);
                    LogUtils.dTag(DemoApplication.TAG, "updateChannelFlag id = " + channelId + " newMask=" + newMask);
                    if ((newMask & 0x0A) == 0x0A) {
                        unregisterChannel(ctx.getChannel());
                    }
                }
            }
        }
    }

    public void unregisterChannel(MediaChannel channel) {
        LogUtils.dTag(DemoApplication.TAG, "unregisterChannel id = " + channel.getChannelId());
        this.channelCtxMap.remove(channel.getChannelId());
    }

    public static CastManager getMgr() {
        if (null == instance) {
            instance = new CastManager();
            //instance.refreshPin();
        }
        return instance;
    }

    public boolean isChannelAvailable() {
        if (channelCtxMap.isEmpty()) {
            return true;
        }

        int channelAva = 0;
        Set<Integer> keys = channelCtxMap.keySet();
        for (int k : keys) {
            MediaChannelCtx ctx = channelCtxMap.get(k);
            if ((ctx.getChannelMask() & MediaChannelCtx.CHANNEL_CLOSED_MASK) == 0) {
                ++channelAva;
            }
        }

        return (channelAva <= 0);
    }

    public void prepareBJCastModule(String deviceName) {
        Properties props = new Properties();
        props.setProperty(BJCastModule.PARA_NAME_DEVICE_NAME, deviceName);
        props.setProperty(BJCastModule.PARA_NAME_MDNS_DEAMON_ENABLE, "0");
        this.castModule = new BJCastModule();
        this.castModule.setImp(new BJCastModuleImp());
        if (castModule.init(props)) {
            LogUtils.dTag(DemoApplication.TAG, "BJCastModule init success");
        } else {
            LogUtils.eTag(DemoApplication.TAG, "BJCastModule init failed");
        }
    }

    public void prepareAirplayModule(String deviceName) {
        this.airplayModule = new AirplayModule();
        this.airplayModule.setImp(new AirplayModuleImp());
        Properties paras = new Properties();
        paras.setProperty(AirplayModule.PARA_NAME_DEVICE_NAME,deviceName);
        paras.setProperty(AirplayModule.PARA_NAME_KEY_FRAMERATE,Integer.toString(60));
        paras.setProperty(AirplayModule.PARA_NAME_KEY_RESOLUTION,"0");
        paras.setProperty(AirplayModule.PARA_NAME_KEY_ROTATION,"0");
        paras.setProperty(BJCastModule.PARA_NAME_MDNS_DEAMON_ENABLE, "1");

        if (airplayModule.init(paras)) {
            LogUtils.dTag(DemoApplication.TAG, "airplayModule init success");
        } else {
            LogUtils.eTag(DemoApplication.TAG, "airplayModule init failed");
        }
    }

    @Subscribe(threadMode = ThreadMode.ASYNC)
    public void onRenameEvent(RenameEvent e){
        LogUtils.iTag("Rename", "onRenameEvent: name:"+e.getName());
        if (airplayModule != null){
            this.airplayModule.rename(e.getName());
        }

        if(castModule != null){
            castModule.updateProp(BJCastModule.PARA_NAME_DEVICE_NAME,e.getName());
        }
    }

    public void refreshPin() {
        if (isEnablePin()) {
            String pincode = "";
            java.util.Random r = new java.util.Random();
            for (int i = 0; i < 4; ++i) {
                int n = (r.nextInt() % 10);
                if (n < 0) {
                    n = n * -1;
                }
                pincode += n;
            }
            //String pincode = "1234";
            setPincode(pincode);
        } else {
            setPincode("");
        }

        CastManager.getMgr().getCastModule().setPin(CastManager.getMgr().getPincode());
        CastManager.getMgr().getAirplayModule().setPassword(CastManager.getMgr().getPincode());
//        ScreenRenderApp.APP.getEventBus().post(new RefreshPinEvent());
    }

    public String getPincode() {
        return pincode;
    }

    public void setPincode(String pincode) {
        this.pincode = pincode;
    }

    public boolean isEnablePin() {
        return isEnablePin;
    }

    public void setEnablePin(boolean enablePin) {
        if (isEnablePin != enablePin) {
            LogUtils.iTag("CONF", "setEnablePin: " + enablePin);
            
            isEnablePin = enablePin;
            refreshPin();
            DemoApplication.APP.getEventBus().post(new RefreshPinEvent());
        }
    }
    public void kickout(MediaChannel channel){
        if(channel instanceof GLScreenRenderChannel){
            getCastModule().kickOut(channel);
        }  else {
            getAirplayModule().kickOut(channel);
        }
    }
}
